/**
 * 将科学计数法的数字转为字符串
 * 说明：运算精度丢失方法中处理数字的时候，如果出现科学计数法，就会导致结果出错
 * @param  num
 */
function toNonExponential (num) {
  if (num == null) {
    return num
  }
  if (typeof num === 'number') {
    var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/)
    return num.toFixed(Math.max(0, (m[1] || '').length - m[2]))
  } else {
    return num
  }
}

/**
 * 加法 - js运算精度丢失问题
 * @param num1  数1
 * @param num2  数2
 */
function accAdd (num1, num2) {
  num1 = Number(num1) || 0
  num2 = Number(num2) || 0
  num1 = toNonExponential(num1)
  num2 = toNonExponential(num2)
  var r1, r2, m
  try {
    r1 = num1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = num2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  m = Math.pow(10, Math.max(r1, r2))
  return (accMul(num1, m) + accMul(num2, m)) / m
}

/**
 * 减法 - js运算精度丢失问题
 * @param num1  数1
 * @param num2  数2
 */
function accSub (num1, num2) {
  num1 = Number(num1) || 0
  num2 = Number(num2) || 0
  num1 = toNonExponential(num1)
  num2 = toNonExponential(num2)
  var r1, r2, m, n
  try {
    r1 = num1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = num2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  m = Math.pow(10, Math.max(r1, r2))
  // 动态控制精度长度
  n = r1 >= r2 ? r1 : r2
  return ((accMul(num1, m) - accMul(num2, m)) / m).toFixed(n)
}

/**
 * 乘法 - js运算精度丢失问题
 * @param num1  数1
 * @param num2  数2
 */
function accMul (num1, num2) {
  num1 = Number(num1)
  num2 = Number(num2)
  if ((!num1 && num1 !== 0) || (!num2 && num2 !== 0)) {
    return null
  }
  num1 = toNonExponential(num1)
  num2 = toNonExponential(num2)
  var n1, n2
  var r1, r2 // 小数位数
  try {
    r1 = num1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = num2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  n1 = Number(num1.toString().replace('.', ''))
  n2 = Number(num2.toString().replace('.', ''))
  return (n1 * n2) / Math.pow(10, r1 + r2)
}

/**
 * 除法 - js运算精度丢失问题
 * @param num1  数1
 * @param num2  数2
 * @param {*} n 保留n位小数
 */
function accDiv (num1, num2, n = 2) {
  num1 = Number(num1)
  num2 = Number(num2)
  if (!num2) {
    return null
  }
  if (!num1 && num1 !== 0) {
    return null
  } else if (num1 === 0) {
    return 0
  }
  num1 = toNonExponential(num1)
  num2 = toNonExponential(num2)
  var n1, n2
  var r1, r2 // 小数位数
  try {
    r1 = num1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = num2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  n1 = Number(num1.toString().replace('.', ''))
  n2 = Number(num2.toString().replace('.', ''))
  return accMul(n1 / n2, Math.pow(10, r2 - r1)).toFixed(n)
}

/**
 * 取余 - js运算精度丢失问题
 * @param num1  数1
 * @param num2  数2
 */
function accMod (num1, num2) {
  num1 = Number(num1)
  num2 = Number(num2)
  if (!num2) {
    return null
  }
  if (!num1 && num1 !== 0) {
    return null
  } else if (num1 === 0) {
    return 0
  }
  let intNum = num1 / num2
  intNum = intNum < 0 ? Math.ceil(num1 / num2) : Math.floor(num1 / num2)
  let intVal = accMul(intNum, num2)
  return accSub(num1, intVal)
}

export default {
  accAdd,
  accSub,
  accDiv,
  accMul,
  accMod
}
